<template>
  <el-col
    class="czr-form-column"
    :class="{
      transparent: transparent,
      'has-width': !!width,
    }"
    :span="span"
    :offset="offset"
    ref="ref_czrFormColumn"
  >
    <el-form-item
      :label="label"
      :label-width="labelWidthCpt"
      :class="{
        ['link_' + link + ($attrs.type ? '_' + $attrs.type : '')]: true,
        required: required !== false,
        'no-label': labelWidth === '0px' && !isValue(label) && !$slots.label,
        'no-label-fit': !labelFit,
        'is-error': !!state.errorMessage,
        [`layout-${layout || formLayout}`]: true,
      }"
      :error="state.errorMessage"
      :required="required"
    >
      <template #label>
        <slot name="label" />
      </template>
      <slot name="czr" :handleValidate="handleValidate">
        <template v-if="link === 'input'">
          <InputCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
            @emitEnter="handleEnter"
          >
            <template v-if="$slots.prefix" #prefix>
              <slot name="prefix" />
            </template>
            <template v-if="$slots.suffix" #suffix>
              <slot name="suffix" />
            </template>
            <template v-if="$slots.prepend" #prepend>
              <slot name="prepend" />
            </template>
            <template v-if="$slots.append" #append>
              <slot name="append" />
            </template>
          </InputCom>
        </template>
        <template v-else-if="link === 'select'">
          <SelectCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
            <slot />
          </SelectCom>
        </template>
        <template v-else-if="link === 'date'">
          <DateCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
            <template #default="{ cell }">
              <slot v-bind="cell" />
            </template>
          </DateCom>
        </template>
        <template v-else-if="link === 'datetime'">
          <DateTimeCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
            <template #default="{ cell }">
              <slot v-bind="cell" />
            </template>
          </DateTimeCom>
        </template>
        <template v-else-if="link === 'time'">
          <TimeCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
          </TimeCom>
        </template>
        <template v-else-if="link === 'cascader'">
          <CascaderCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
            <template v-if="$slots.default" #default="{ node, data }">
              <slot v-bind="{ node, data }" />
            </template>
          </CascaderCom>
        </template>
        <template v-else-if="link === 'switch'">
          <SwitchCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
          </SwitchCom>
        </template>
        <template v-else-if="link === 'radio'">
          <RadioCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
          </RadioCom>
        </template>
        <template v-else-if="link === 'checkbox'">
          <CheckboxCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
          </CheckboxCom>
        </template>
        <template v-else-if="link === 'number'">
          <NumberCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
            @emitEnter="handleEnter"
          >
            <template v-if="$slots.prefix" #prefix>
              <slot name="prefix" />
            </template>
            <template v-if="$slots.suffix" #suffix>
              <slot name="suffix" />
            </template>
            <template v-if="$slots.prepend" #prepend>
              <slot name="prepend" />
            </template>
            <template v-if="$slots.append" #append>
              <slot name="append" />
            </template>
          </NumberCom>
        </template>
        <template v-else-if="link === 'input-number'">
          <InputNumberCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
            @emitEnter="handleEnter"
          >
            <template v-if="$slots.prefix" #prefix>
              <slot name="prefix" />
            </template>
            <template v-if="$slots.suffix" #suffix>
              <slot name="suffix" />
            </template>
          </InputNumberCom>
        </template>
        <template v-else-if="link === 'tree-select'">
          <TreeSelectCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
            <template v-if="$slots.default" #default="prop">
              <slot name="default" v-bind="prop" />
            </template>
          </TreeSelectCom>
        </template>
        <template v-else-if="link === 'upload'">
          <UploadCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          />
        </template>
        <template v-else-if="link === 'rich'">
          <RichCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="
              (val) => {
                $emit('update:param', val), handleValidate(val)
              }
            "
          >
          </RichCom>
        </template>
      </slot>
      <div v-if="unit" class="unit">{{ unit }}</div>
      <slot name="unit" />
    </el-form-item>
  </el-col>
</template>

<script setup lang="ts">
defineOptions({
  name: 'CzrFormColumn',
})
import {
  computed,
  onMounted,
  ref,
  reactive,
  watch,
  getCurrentInstance,
  ComponentInternalInstance,
  inject,
  onBeforeUnmount,
} from 'vue'
import InputCom from './czr-form-link/input.vue'
import InputNumberCom from './czr-form-link/input-number.vue'
import SelectCom from './czr-form-link/select.vue'
import DateCom from './czr-form-link/date.vue'
import DateTimeCom from './czr-form-link/datetime.vue'
import TimeCom from './czr-form-link/time.vue'
import CascaderCom from './czr-form-link/cascader.vue'
import SwitchCom from './czr-form-link/switch.vue'
import RadioCom from './czr-form-link/radio.vue'
import CheckboxCom from './czr-form-link/checkbox.vue'
import NumberCom from './czr-form-link/number.vue'
import TreeSelectCom from './czr-form-link/tree-select.vue'
import UploadCom from './czr-form-link/upload.vue'
import RichCom from './czr-form-link/rich.vue'

import { v4 } from 'uuid'
const props = defineProps({
  span: { type: Number, default: 6 }, // 栅格
  offset: { type: Number, default: 0 }, // 栅格
  param: {}, // 绑定值
  label: { type: String, default: '' }, // 标题
  required: { default: false }, // 必填项
  labelWidth: { type: String, default: '' }, // 标题宽度
  link: {
    type: String,
    default: 'input',
    validator(val: string) {
      return [
        'cascader',
        'checkbox',
        'date',
        'datetime',
        'input',
        'radio',
        'select',
        'switch',
        'dept',
        'time',
        'upload',
        'number',
        'input-number',
        'tree-select',
        'rich',
      ].includes(val)
    },
  }, // 类型，为了避免与原生type字段重复
  rules: { type: Array, default: () => [] }, // 自定义规则
  maxLength: { type: Number, default: null }, // 最大长度
  minLength: { type: Number, default: null }, // 最小长度
  defaultErrorMsg: { default: null }, // 默认校验错误提示
  unit: { default: '', type: String }, // 单位
  otherInfo: {}, // 其他信息
  layout: {},
  transparent: { default: false },
  width: { default: '' },
  labelFit: { default: true },
})
const attrs = (getCurrentInstance() as ComponentInternalInstance).attrs
const state = reactive({
  errorMessage: null,
  uuid: '',
})
const ref_czrFormColumn: any = ref(null)
const isValue = (val: any) => {
  if (
    val === null ||
    val === undefined ||
    (typeof val === 'string' && val.trim() === '') ||
    (typeof val === 'object' && val?.length === 0)
  ) {
    return false
  }
  return true
}
const labelCpt = computed(() => {
  return props.label.replace(/[:：]([^:：]*)$/, '')
})
const rulesCpt = computed(() => {
  const r = [...props.rules]
  if (isValue(props.minLength)) {
    r.unshift({
      handle: (val: any) =>
        !isValue(val) ||
        (isValue(val) && String(val).length >= props.minLength),
      message: `内容过短，字数需大于等于${props.minLength}`,
    })
  }
  if (isValue(props.maxLength)) {
    r.unshift({
      handle: (val: any) =>
        !isValue(val) ||
        (isValue(val) && String(val).length <= props.maxLength),
      message: `内容过长，字数需小于等于${props.maxLength}`,
    })
  } else if (
    props.link === 'input' &&
    attrs.type === 'textarea' &&
    isValue(attrs.maxlength)
  ) {
    r.unshift({
      handle: (val: any) =>
        !isValue(val) ||
        (isValue(val) && String(val).length <= Number(attrs.maxlength)),
      message: `内容过长，字数需小于等于${attrs.maxlength}`,
    })
  }
  const doStr = ['input', 'number', 'input-number', 'rich'].includes(props.link)
    ? '输入'
    : '选择'
  if (
    props.required !== false &&
    !props.rules.some((v: any) => v.type === 'default')
  ) {
    r.unshift({
      handle: (val: any) => isValue(val),
      message: props.defaultErrorMsg ?? `请${doStr}${labelCpt.value}`,
    })
  }
  return r
})
const handleValidate = (val: any = undefined, val2: any = undefined) => {
  state.errorMessage = null
  for (let i = 0; i < rulesCpt.value.length; i++) {
    const item: any = rulesCpt.value[i]
    if (!item.handle(val === undefined ? props.param : val)) {
      state.errorMessage = item.message
      break
    }
  }
  return state.errorMessage
}
const formLayout = inject('form-layout', 'x')
const handleEnterFunc = inject('handle-enter', () => {})
const formLabelWidth = inject('form-label-width', '')
const labelWidthCpt = computed(() => {
  if (props.labelWidth) {
    return props.labelWidth
  }
  if (formLabelWidth) {
    return formLabelWidth
  }
  return 'auto'
})
const handleEnter = () => {
  handleEnterFunc?.()
}
const reset = () => {
  state.errorMessage = null
}
watch(
  () => state.errorMessage,
  (n) => {
    const p =
      ref_czrFormColumn.value?.$el?.getElementsByClassName('el-form-item')?.[0]
    if (n) {
      setTimeout(() => {
        const e = p
          ?.getElementsByClassName('el-form-item__content')?.[0]
          ?.getElementsByClassName('el-form-item__error')?.[0]
        if (e?.clientHeight) {
          p.style.marginBottom = `${e.clientHeight + 4}px`
        }
      }, 200)
    } else {
      p.style.marginBottom = props.transparent ? 0 : '18px'
    }
  },
)
onMounted(() => {
  state.uuid = v4()
  const formChildrenMap: any = inject('czr-form-children-map', new Map())
  let flag = true
  const deep = (dom) => {
    if (dom.className === 'hidden-columns') {
      flag = false
    }
    if (dom.parentElement) {
      deep(dom.parentElement)
    }
  }
  deep(ref_czrFormColumn.value.$parent.$el)
  if (flag) {
    formChildrenMap.set(state.uuid, ref_czrFormColumn.value.$parent)
  }
})
onBeforeUnmount(() => {
  const formChildrenMap: any = inject('czr-form-children-map', new Map())
  formChildrenMap.delete(state.uuid)
})
defineExpose({
  reset,
  handleValidate,
  handleEnter,
})
</script>

<style scoped lang="scss">
.czr-form-column {
  &.has-width {
    width: v-bind(width);
    max-width: v-bind(width);
    flex: unset;
  }
  &.transparent {
    :deep(.el-form-item) {
      margin-bottom: 0;
      .el-input__wrapper {
        background-color: transparent;
        box-shadow: none;
      }
    }
  }
  :deep(.el-form-item) {
    &.link_time {
      margin-top: 1px;
    }
    &.no-label {
      .el-form-item__label {
        display: none;
      }
    }
    &.no-label-fit {
      .el-form-item__label-wrap {
        margin-left: 0 !important;
      }
    }
    &.layout-y {
      .el-form-item__label {
        justify-content: flex-start;
        padding-left: 0;
      }
    }
    &.layout-x {
      display: flex;
    }
    $textColor: #576275;
    .el-form-item__label {
      line-height: 1;
      text-align: right;
      display: flex;
      align-items: center;
      padding-left: 10px;
      color: $textColor;
      font-weight: normal;
    }
    .el-form-item__content {
      flex-wrap: unset;
      > div:first-child {
        flex: 1;
        height: 100%;
      }
      .unit {
        margin-left: 6px;
        font-size: 14px;
        font-family:
          PingFang SC-Regular,
          PingFang SC;
        font-weight: 400;
        color: #606266;
      }
      .is-disabled {
        .el-select__selected-item:not(.is-transparent),
        .el-input__inner,
        .el-radio__label,
        .el-checkbox__label,
        .el-range-input,
        .el-range-separator,
        .el-textarea__inner {
          color: $textColor;
          -webkit-text-fill-color: $textColor;
        }
      }
    }
  }
}
</style>
